#include "RoServerModule.h"


RoServerModule::RoServerModule(QObject * parent, bool logTraffic):
    super(parent)
{
    setName("ROUTER");

    this->logTraffic = logTraffic;

    transport = new QTcpServer(this);

    connect(transport, SIGNAL( newConnection() ),
            this, SLOT( acceptNewConnection() ));
}


RoServerModule::~RoServerModule()
{
    // QTcpServer is a QObject and a child of this,
    // so we don't need to delete it
}


RoStanzaId RoServerModule::sendStanza(RoStanza stanza)
{
    QString dest = stanza.to();
    if ( dest.isEmpty() )
    {
        QString source = stanza.from();
        foreach ( RoXmlStream * stream, clients.streams() )
        {
            if ( stream != clients.stream(source) ) // не посылать тому, от кого получили
                stream->writeStanza(stanza);
        }
    }
    else if ( dest == "ROUTER" )
    {
        recieve_own_stanza(stanza);
    }
    else
    {
        QPointer<RoXmlStream> stream = clients.stream(dest);
        if ( stream )
        {
            stream->writeStanza(stanza);
        }
        else
        {
            if ( stanza.documentElement().tagName() == "iq" )
                sendStanza(RoIqStanza::createError( RoIqStanza::RecipientUnknown,
                                                    stanza.from(),
                                                    stanza.id(),
                                                    name() ));
        }
    }

    return stanza.id(); // в случае неудачи будет пустым, а это то, что надо
}


void RoServerModule::presenceEvent(RoXmlStream * stream, RoInfoStanza stanza)
{
    QString status = stanza.bodyAttribute("status");
    if ( status == "available" )
    {
        clients.insert(stanza.from(), stream);
    }
    else if ( status == "unavailable" )
    {
        clients.remove(stanza.from());
    }
}


void RoServerModule::recieve_stanza(RoStanza stanza)
{
    if ( stanza.to().isEmpty() ) // без адресата
    {
        recieve_own_stanza(stanza); // взять себе
        sendStanza(stanza); // и показать другим
    }
    else if ( stanza.to() == "ROUTER" ) // нам
    {
        recieve_own_stanza(stanza); // взять себе
    }
    else // кому-то ещё
    {
        sendStanza(stanza); // показать всем
    }
}


void RoServerModule::acceptNewConnection()
{
    if ( not transport->hasPendingConnections() )
        return;

    QPointer<QTcpSocket> clSocket = transport->nextPendingConnection();
    QPointer<RoXmlStream> clStream = new RoXmlStream(this, clSocket, RoXmlStream::LogTraffic * logTraffic);
    clStream->openStream();
    clientStreams.insert(clSocket->socketDescriptor(), clStream);

    connect(clSocket, SIGNAL( disconnected() ),
            this, SLOT( mournClient() ));
    connect(clStream, SIGNAL( stanza(RoStanza) ),
            this, SLOT( recieve_stanza(RoStanza) ));

    qDebug() << "Client connected:" << clSocket->peerAddress() << clSocket->peerPort();
    emit clientConnected(clStream);
}


void RoServerModule::mournClient()
{
    QPointer<QTcpSocket> clSocket = qobject_cast<QTcpSocket*>(sender());
    if ( not clSocket )
        return;

    RoXmlStream * stream = 0;
    foreach ( RoXmlStream * s, clients.streams() )
    {
        if ( s->device() == clSocket )
        {
            stream = s;
            break;
        }
    }

    if ( not stream )
    {
        // такое вполне может быть,
        // например, если они успели корректно отключиться
        // или если модуль не объявил ни одного компонента
        return;
    }

    foreach ( QString name, clients.components(stream) )
    {
        // разошлём сами от их имени презенс отсутствия
        recieve_stanza( RoInfoStanza("presence")
                        .setAttribute("from", name)
                        .setBodyAttribute("status", "unavailable") );
    }

    qDebug() << "Client gone:" << clSocket->peerAddress() << clSocket->peerPort();
}
